package com.gpagers.core;

import static org.quartz.impl.matchers.KeyMatcher.keyEquals;

import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import org.quartz.CronScheduleBuilder;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.Trigger;
import org.quartz.Trigger.TriggerState;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.quartz.impl.StdSchedulerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 单例ScheduleInstance<tr>
 * 主要提供了quartz的操作，包括了定义任务(Defining a Job)，定义触发器(Defining a trigger)，调度任务(Defining and Scheduling a Job)，
 * 更新任务(Updating an existing Job)，暂停任务(interrupt a job)，删除任务(Unscheduling a Job)，关闭调度线程(Shutting Down a Scheduler)等等。
 * <br>
 * <p>根据quartz官方的说法，多个“调度器”需要多个quartz配置文件。它们引用同一个数据库不会出现问题，它们使用不同的store方式也没有问题，但是你需要指定非重复的调度器名字(schedul name),
 * 因此在如下的方法中，需要指明调度线程的名字</p>
 * 
 * <p>本类代码来源于quartz官网 <a href="http://www.quartz-scheduler.org/documentation/quartz-2.2.x/cookbook/">http://www.quartz-scheduler.org/documentation/quartz-2.2.x/cookbook/</a></p>
 * 
 * <p>
 *  如下是一个标准的配置可供参考:<br>
 *  org.quartz.scheduler.instanceName = MyScheduler<br>
	org.quartz.threadPool.threadCount = 6<br>
	#org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore 内存store<br>
	org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX #数据库store<br>
	org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate<br>
	org.quartz.jobStore.tablePrefix = qrtz_<br>
	org.quartz.jobStore.dataSource = datasource<br>
	org.quartz.dataSource.datasource.driver: com.mysql.jdbc.Driver<br>
	org.quartz.dataSource.datasource.URL:jdbc:MySQL://localhost:3306/quartz?useUnicode=true&characterEncoding=UTF-8<br>
	org.quartz.dataSource.datasource.user: root<br>
	org.quartz.dataSource.datasource.password: 123456<br>
	org.quartz.dataSource.datasource.maxConnections: 10<br>
 * </p>
 * @author van
 * @time 2014年11月3日 下午7:17:30
 */
public class ScheduleInstance {

    public static final String DEFAULT_DESCRIPTION = "NONE";

    Logger logger = LoggerFactory.getLogger(ScheduleInstance.class);

    private static ScheduleInstance scheduleInstance;
    private Scheduler               scheduler;

    private ScheduleInstance() {
    };

    public static ScheduleInstance getInstance() {
        if (scheduleInstance == null) {
            scheduleInstance = new ScheduleInstance();
        }
        return scheduleInstance;
    }

    /**
     * 根据名字获取调度器
     * @param name
     * @return
     * @throws IOException 
     */
    public Scheduler getScheduler() {
        if (scheduler != null)
            return scheduler;
        try {
            InputStream inputStream = ScheduleInstance.class.getClassLoader()
                .getResourceAsStream("quartz.properties");
            Properties p = new Properties();
            p.load(inputStream);
            SchedulerFactory sf = new StdSchedulerFactory(p);
            scheduler = null;
            scheduler = sf.getScheduler();
            scheduler.start();
        } catch (Exception e1) {
            logger.error("load quartz.properties failed!", e1);
        }

        return scheduler;
    }

    /**
     * IS_NONCONCURRENT 和 IS_UPDATE_DATA在job定义时注解上面设置
     * @param classzz
     * @param name
     * @param group
     * @param String ... args 顺序 DESCRIPTION，IS_DURABLE，REQUESTS_RECOVERY
     * @return
     */
    public JobDetail defineJob(Class<? extends Job> classzz, String name, String group,
                               String... args) {
        //init null data
        Map<String, Object> map = transformArgs(args);
        JobDetail job = JobBuilder.newJob(classzz).withIdentity(name, group)
            .withDescription(map.get("DESCRIPTION").toString())
            .storeDurably("0".equals(map.get("IS_DURABLE")) ? false : true)
            .requestRecovery("0".equals(map.get("REQUESTS_RECOVERY")) ? false : true).build();
        return job;
    }

    /**
     * 定义一个cronTrigger
     * @param name
     * @param group
     * @param expression
     * @param startTime
     * @param endTime
     * @param priority
     * @return
     * @throws SchedulerException
     */
    public Trigger defineCronTrigger(String name, String group, String expression, Date startTime,
                                     Date endTime, int priority) throws Exception {
        Trigger trigger = TriggerBuilder.newTrigger().withIdentity(name, group)
            .withSchedule(CronScheduleBuilder.cronSchedule(expression)).startAt(startTime)
            .endAt(endTime).withPriority(priority).build();
        return trigger;
    }

    /**
     * 
     * @param jobDetail
     * @param trigger
     * @param scheduler
     * @throws SchedulerException
     */
    public void schedulJob(JobDetail jobDetail, Trigger trigger) throws Exception {
        Scheduler scheduler = getScheduler();
        try {
            scheduler.scheduleJob(jobDetail, trigger);
        } catch (Exception e) {
            logger.error("{}", e.toString(), e);
            throw e;
        }

    }

    /**
     * 暂停一个任务
     * @param jobName
     * @param jonGroup
     * @param schedulerName
     * @throws SchedulerException
     */
    public void pauseJob(String jobName, String jonGroup) throws SchedulerException {
        Scheduler scheduler = getScheduler();
        try {
            scheduler.pauseJob(new JobKey(jobName, jonGroup));
        } catch (SchedulerException e) {
            logger.error("{}", e.toString(), e);
            throw e;
        }
    }

    /**
     * 恢复一个任务
     * @param jobName
     * @param jonGroup
     * @param schedulerName
     * @throws SchedulerException
     */
    public void resumeJob(String jobName, String jonGroup) throws SchedulerException {
        Scheduler scheduler = getScheduler();
        try {
            scheduler.resumeJob(new JobKey(jobName, jonGroup));

        } catch (SchedulerException e) {
            logger.error("{}", e.toString(), e);
            throw e;
        }
    }

    public void pauseTrigger(String name, String group) throws SchedulerException {
        Scheduler scheduler = getScheduler();
        try {
            scheduler.pauseTrigger(new TriggerKey(name, group));
        } catch (SchedulerException e) {
            logger.error("{}", e.toString(), e);
            throw e;
        }
    }

    public void resumeTrigger(String name, String group) throws SchedulerException {
        Scheduler scheduler = getScheduler();
        try {
            scheduler.resumeTrigger(new TriggerKey(name, group));
        } catch (SchedulerException e) {
            logger.error("{}", e.toString(), e);
            throw e;
        }
    }

    public void deleteTrigger(String name, String group) throws SchedulerException {
        Scheduler scheduler = getScheduler();
        try {
            scheduler.unscheduleJob(new TriggerKey(name, group));
        } catch (SchedulerException e) {
            logger.error("{}", e.toString(), e);
            e.printStackTrace();
            throw e;
        }
    }

    /**
     * 更新一个job
     * @param jobDetailExt
     * @param scheduler
     * @throws ClassNotFoundException
     * @throws SchedulerException
     */
    public void updateJob(JobDetail jobDetail, Scheduler scheduler) {
        try {
            scheduler.addJob(jobDetail, true);
        } catch (SchedulerException e) {
            logger.error("", e);
        }
    }

    /**
     * 更新trigger
     * @param oldtriggerName
     * @param oldtriggerGroup
     * @param trigger
     * @throws SchedulerException 
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public void updateTriggerExp(String oldtriggerName, String oldtriggerGroup, String expression,
                                 Scheduler scheduler) throws SchedulerException {
        Trigger oldTrigger = scheduler.getTrigger(new TriggerKey(oldtriggerName, oldtriggerGroup));
        TriggerBuilder tb = oldTrigger.getTriggerBuilder();
        Trigger newTrigger = tb.withSchedule(CronScheduleBuilder.cronSchedule(expression)).build();
        scheduler.rescheduleJob(oldTrigger.getKey(), newTrigger);
    }

    public String getTriggerState(String name, String group) {
        Scheduler scheduler = getScheduler();
        TriggerKey key = new TriggerKey(name, group);
        try {
            TriggerState st = scheduler.getTriggerState(key);
            return st.name();
        } catch (SchedulerException e) {
            logger.error("", e);
            return null;
        }
    }

    /**
     * 立即执行一次已经存在的job,自定义了触发器，name为 “schedulAnExistJobNow” group为uuid
     * @param jobName
     * @param jobGroup
     * @param scheduler
     * @throws SchedulerException 
     */
    public void schedulAnExistJobNow(String jobName, String jobGroup) throws SchedulerException {
        Scheduler _scheduler = getScheduler();
        String _triggerName = UuidUtils.getUUID();
        String _triggerGroup = UuidUtils.getUUID();
        Trigger trigger = TriggerBuilder.newTrigger().withIdentity(_triggerName, _triggerGroup)
            .startNow().forJob(new JobKey(jobName, jobGroup)).build();
        _scheduler.scheduleJob(trigger);
    }

    public void schedulAnExistJobNow(JobDetail jobDetail) throws SchedulerException {
        Scheduler _scheduler = getScheduler();
        String triggerName = UuidUtils.getUUID();
        String triggerGroup = UuidUtils.getUUID();

        Trigger trigger = TriggerBuilder.newTrigger().withIdentity(triggerName, triggerGroup)
            .startNow().forJob(jobDetail).build();
        _scheduler.scheduleJob(trigger);
    }

    /**
     * 根据传入的数组转换为Map
     * @param args
     * @return
     */
    private Map<String, Object> transformArgs(String[] args) {
        Map<String, Object> map = new HashMap<String, Object>();

        //init map
        map.put("DESCRIPTION", DEFAULT_DESCRIPTION);
        map.put("IS_DURABLE", true);
        map.put("REQUESTS_RECOVERY", true);

        if (args == null || args.length == 0) {
            return map;
        }
        for (int i = 0; i < args.length; i++) {
            switch (i) {
                case 0:
                    map.put("DESCRIPTION", args[i]);
                    break;
                case 1:
                    map.put("IS_DURABLE", args[i]);
                    break;
                case 2:
                    map.put("REQUESTS_RECOVERY", args[i]);
                    break;
                default:
                    break;
            }
        }
        return map;
    }

    public void removeListener(String triggerName, String triggerGroup) throws SchedulerException {
        Scheduler _scheduler = getScheduler();
        _scheduler.getListenerManager().removeTriggerListenerMatcher(triggerName,
            keyEquals(new TriggerKey(triggerName, triggerGroup)));
    }

}
